package com.gitee.liuzhi.common.util;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.experimental.UtilityClass;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * 常用的jdk8 stream的工具类
 */
@UtilityClass
public class StreamUtil {


    /**
     * @param list 集合
     * @param mapping 映射
     * @param <T> 源数据泛型
     * @param <R> 返回泛型
     */
    public <T, R> Set<R> fetch(List<T> list, Function<T, R> mapping) {
        if (CollectionUtils.isEmpty(list)) {
            return Sets.newHashSet();
        }
        return list.stream().map(mapping).collect(Collectors.toCollection(LinkedHashSet::new));
    }

    @SafeVarargs
    public <T, R> Set<R> fetchAll(List<T> list, Function<T, R>... mappings) {
        Set<R> fetch = new HashSet<>();
        for (Function<T, R> mapping : mappings) {
            fetch.addAll(fetch(list, mapping));
        }
        return fetch;
    }

    /**
     * 抽取集合中的某个属性为一个集合
     *
     * @param list    集合
     * @param mapping 查询属性的方法
     * @param <E>     集合中的元素类型
     * @param <T>     属性类型
     * @return 属性的集合
     */
    public <E, T> List<T> fetchProperty(List<E> list, Function<E, T> mapping) {
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        return list.stream().map(mapping)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 抽取集合中的某个属性为一个集合
     *
     * @param list    集合
     * @param mapping 查询属性的方法
     * @param predicate 筛选条件
     * @param <E>     集合中的元素类型
     * @param <T>     属性类型
     * @return 属性的集合
     */
    public <E, T> List<T> fetchProperty(List<E> list, Function<E, T> mapping,Predicate<E> predicate) {
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        return list.stream()
                .filter(predicate)
                .map(mapping)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 根据key 进行过滤
     * @param keyExtractor keyExtractor
     * @return T
     * @param <T> T
     */
    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
    public static <T> Predicate<T> distinctFilterByKey(Function<? super T, ?> keyExtractor) {
        ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>(16);
        return t -> map.containsKey(keyExtractor.apply(t))||map.put(keyExtractor.apply(t), Boolean.TRUE) != null;
    }



    /**
     * 抽取集合中的某个属性为一个集合
     *
     * @param list    集合
     * @param mapping 查询属性的方法
     * @param <E>     集合中的元素类型
     * @param <T>     属性类型
     * @return 属性的集合
     */
    public <E, T> Set<T> fetchPropertySet(List<E> list, Function<E, T> mapping) {
        if (CollectionUtils.isEmpty(list)) {
            return Sets.newHashSet();
        }
        return list.stream().map(mapping)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
    }

    /**
     * 根据某个属性出现的次数分组聚合
     * [{a:甲}, {a:乙}, {b:丙}, {b:丁}, {c:戊} ]
     * a和b出现两次, c出现一次
     * 返回: {2:[a,b],1:[c]}
     *
     * @param list    元素集合
     * @param mapping 查询属性的方法
     * @param <E>     元素类型
     * @param <T>     属性类型
     * @return 属性出现次数对应属性集合的map
     */
    public <E, T> Map<Long, List<T>> groupByPropertyNum(List<E> list, Function<E, T> mapping) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }

        return list.stream().map(mapping)
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                .entrySet().stream()
                .collect(Collectors.groupingBy(Map.Entry::getValue, HashMap::new,
                        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
    }

    /**
     * 转为map, 注意:key重复时, 取第一次key对应的value
     */
    @SafeVarargs
    public <E, K, V> Map<K, V> toMap(Collection<E> list, Function<E, K> keyMapping, Function<E, V> valueMapping, Predicate<? super E>... filters) {
        if (CollectionUtils.isEmpty(list)){
            return Collections.emptyMap();
        }
        Stream<E> stream = list.stream();
        if (ArrayUtils.isNotEmpty(filters)) {
            for (Predicate<? super E> filter : filters) {
                stream = stream.filter(filter);
            }
        }
        return stream.collect(Collectors.toMap(keyMapping, valueMapping, ObjectUtils::firstNonNull));
    }

    public <E, K, V> Map<K, V> toMap(Collection<E> list, Function<E, K> keyMapping, Function<E, V> valueMapping, BinaryOperator<V> mergeFunction) {
        if (CollectionUtils.isEmpty(list)){
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(keyMapping, valueMapping, mergeFunction));
    }

    /**
     * 一维list只取第一层
     */
    public <E> List<E> listToFirstLevel(List<E> list,
                                  Function<E, String> nodeFunction,
                                  Function<E, String> parentFunction,
                                  Function<E, Long> sortBy,
                                  BiConsumer<E, Boolean> hasChild) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        //取第一层节点
        List<E> firstLevel = getFirstLevel(list, nodeFunction, parentFunction);
        firstLevel.sort(Comparator.comparingLong(sortBy::apply));
        Map<String, List<E>> parent2ChildList = list.stream()
                //排除刚才取了的第一层节点
                .filter(a -> !fetchProperty(firstLevel, nodeFunction).contains(nodeFunction.apply(a)))
                .collect(Collectors.groupingBy(parentFunction));
        firstLevel.forEach(a ->forEach(parent2ChildList, a, nodeFunction, hasChild));
        return firstLevel;
    }

    /**
     * 获取第一级, 可能是顶点, 也可能是多个第一级节点
     */
    private  <E> List<E> getFirstLevel(List<E> list,
                                     Function<E, String> nodeFunction,
                                     Function<E, String> parentFunction) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        List<String> nodes = fetchProperty(list, nodeFunction);
        List<String> parentNods = fetchProperty(list, parentFunction);
        List<String> topNodes = ListUtils.subtract(parentNods, nodes);

        Predicate<E> predicate;
        if (CollectionUtils.isEmpty(topNodes)) {
            //如果没有顶点, 可能顶点等于本身
            predicate = a -> Objects.equals(nodeFunction.apply(a), parentFunction.apply(a));
        } else {
            predicate = a -> topNodes.contains(parentFunction.apply(a));
        }
        return list.stream().filter(predicate).collect(Collectors.toList());
    }


    /**
     * 递归给子类赋值集合
     */
    private <E> void forEach(Map<String, List<E>> allNode,
                             E parentNode,
                             Function<E, String> nodeFunction,
                             BiConsumer<E, Boolean> hasChild) {
        List<E> childNodeList = allNode.get(nodeFunction.apply(parentNode));
        if (CollectionUtils.isNotEmpty(childNodeList)) {
            hasChild.accept(parentNode, true);
        } else {
            hasChild.accept(parentNode, false);
        }
    }

    /**
     * 集合 分组后执行操作（如：根据集合某个字段 分组后转换为目标对象）
     *
     * @param list 数据
     * @param groupKey 分组键
     * @param mapFactory 转换方法
     * @param <E> 数据泛型
     * @param <K> 键泛型
     * @param <T> 返回泛型
     * @return 分组后数据 filter groupKey is null
     */
    public <E, K, T> Map<K, List<T>> groupMapping(List<E> list, Function<E, K> groupKey, Function<List<E>, List<T>> mapFactory) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        Collector<E, ?, List<T>> mapping = Collectors.mapping(Function.identity(), Collectors.collectingAndThen(Collectors.toList(), mapFactory));
        return list.stream().filter(e -> ObjectUtils.allNotNull(groupKey.apply(e))).collect(Collectors.groupingBy(groupKey, mapping));
    }

    /**
     * @param dataList 源数据集合
     * @param groupFunction 分组映射
     * @param reducing 合并操作
     * @param <T> 数据泛型
     * @param <K> 分组键泛型
     */
    public <T, K> List<T> groupReducing(List<T> dataList, Function<T, K> groupFunction, BinaryOperator<T> reducing) {
        return dataList.stream()
            .collect(Collectors.groupingBy(groupFunction, LinkedHashMap::new, Collectors.reducing(reducing)))
            .values()
            .stream()
            .map(e -> e.orElse(null))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    }

    @SafeVarargs
    public <T> List<T> mergeDistinctList(Function<T, Object> distinctKeyFunc, List<T>... list) {
        List<T> mergeList = Arrays.stream(list).flatMap(Collection::stream).collect(Collectors.toList());
        List<T> resultList = new ArrayList<>();
        Set<Object> keys = new HashSet<>();
        for (T t : mergeList) {
            Object distinctKey = distinctKeyFunc.apply(t);
            if (keys.contains(distinctKey)) {
                continue;
            }
            keys.add(distinctKey);
            resultList.add(t);
        }
        return resultList;
    }

    /**
     * 获取集合中的第一个元素
     */
    public <T> T findFirst(List<T> dataList) {
        if (CollectionUtils.isEmpty(dataList)) {
            return null;
        }
        return dataList.stream().findFirst().orElse(null);
    }

    public <T, R> List<R> copyList(List<T> dataList, Function<T, R> function) {
        if (CollectionUtils.isEmpty(dataList)) {
            return Collections.emptyList();
        }
        return dataList.stream().map(function).collect(Collectors.toList());
    }

    /**
     * 合并两个集合 返回第一集合
     *
     * @author xuguang
     */
    public <E> List<E> addAll(List<E> a, List<E> b) {
        if (CollectionUtils.isEmpty(a)) {
            return b;
        }
        if (CollectionUtils.isEmpty(b)) {
            return a;
        }
        a.addAll(b);
        return a;
    }

    /**
     * reduce操作
     * @param list 操作列表
     * @param mapper 映射关系
     * @param defaultValue 默认值
     * @param binaryOperator reduce操作
     * @param <T> 数据泛型
     * @param <R> 操作泛型
     */
    public <T, R extends Number> R reduce(List<T> list, Function<T, R> mapper, R defaultValue, BinaryOperator<R> binaryOperator) {
        return list.stream().map(mapper).reduce(defaultValue, binaryOperator);
    }

}
